home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 013 / hdtest.arc / HD.ASM next >
Assembly Source File  |  1985-03-05  |  22KB  |  734 lines

  1.     page    ,132
  2. Title        * HD *  -  Hard Disk Seek and Read, Test Program
  3. Subttl        Written, Directed, and Copyrighted by R. Steincross Feb 1985
  4.  
  5. Comment    ~    This program honors several slash commands as shown below to determine the 
  6.     performance of a hard disk drive or controller.  Normally, only the first slash 
  7.     command encountered is executed.  If a "/2" is detected on the command line, then 
  8.     all testing is directed to the second drive of the controller.  If no params are 
  9.     entered, then the help screen is displayed.  (commands listed in search priority)
  10.  
  11.     /2    Use the second drive instead of the first.
  12.     /I    Information about the drive(s) is reported.
  13.     /S n    Seek test from 0 to 'n' and Recal from 'n' to 0.
  14.     /R n    Read 'n' Tracks and report the time taken.
  15.     /P    Park the drive on the last track.
  16.     /?    Help screen displayed.  (also, if no parameter specified)
  17.     ~
  18.     cr    equ    0Dh
  19.     lf    equ    0Ah
  20.     eof    equ    "$"    ;used to terminate strings going to CRT
  21.     bell    equ    07
  22.     DOS    equ    21h    ;this is the function interrupt into DOS
  23.     DISK    equ    13h    ;this is the interrupt to the Disk BIOS
  24.  
  25. code_seg    segment        ;begin code segment
  26.     assume    cs:code_seg, ds:code_seg
  27.  
  28.     org    080h        ;this picks up the command line
  29. cmd_len        db    ?
  30. cmd_line    db    ?
  31.  
  32.     org    100h        ;COM files org at 100h
  33. HD    proc    near        
  34. entry:    jmp    go
  35.  
  36. drive_sel    db    80h        ;this is drive 1, make 81 for drive 2
  37.         db    "******"    ;this is the ASCII buffer for numeric output,
  38. ascii        db    "*",eof        ;label points to rightmost position in buffer
  39. number        dw    0000        ;written to by GET_Num
  40. cs_save        dw    0000        ;keep a copy of our Code Segment
  41. leading0    db    '0'        ;used in Time_Display routine
  42.  
  43.     ; The following fields are set when determined by GET_Params:
  44. num_drives    db    00        ;  1..D
  45. num_tracks    dw    0000        ;  0..T        gets altered by get_param
  46. num_heads    db    00        ;  0..H-1
  47. num_sectors    db    00        ;  1..S
  48.  
  49.     ; These storage locations are used by Time_Start, _Stop, & _Display
  50. num_minutes    db    00    ; 0..59 minutes
  51. num_seconds    db    00    ; 0..59 seconds, used in the get_time routines
  52. num_100ths    db    00    ; 0..99, this is 100th of a second
  53. ; num_time    dw    0000    ; 0..32000, represents 32seconds, in 1/100ths
  54.  
  55.     db    25 dup (' Stack .')
  56. stack    dw    1111h
  57.  
  58.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  59.  
  60.     org    200h    ;this keeps the code in a constant place for testing
  61.  
  62. go:
  63.     mov    sp,offset stack
  64.     push    cs            ;This is a Must!
  65.     mov    ax,cs
  66.     mov    ds,ax        ;all segment registers are AT the Code Segment on start-up
  67.     mov    es,ax
  68.     mov    CS_Save,AX        ;used when we exit this program
  69.     sti                ;enable interrupts
  70.      mov    dx,offset sign_on    ;Show the sign-on message
  71.     call    crt
  72.     call    upper            ;convert command line to all UPPER case
  73.  
  74.             ; * * * Second Drive Selection * * *
  75.     mov    ah,"2"
  76.     call    find            ;see if this is on the command line
  77.     jne    check_F            ;NO, go on to next check
  78.     inc    drive_sel        ;select next drive
  79.     mov    dx,offset msg_2nd
  80.     call    crt
  81.  
  82. check_F:        ; * * * FLOPPY Drive Selection * * * 
  83.     mov    ah,"F"
  84.     call    find
  85.     jne    check_I
  86.     mov    al,drive_sel
  87.     and    al,00000001b        ;mask off the high (hard disc) bit
  88.     mov    drive_sel,al
  89.     mov    dx,offset msg_floppy
  90.     call    crt
  91.  
  92. check_I:        ; * * * INFOrmation Report * * *
  93.                 ;we now know whether to use drive 1 or 2, so...
  94.     call    GET_Params        ;get the parameters for selected drive
  95.     mov    ah,"I"
  96.     call    find
  97.     jne    check_S            ;go on to next test
  98.     call    INFO            ;report the Facts to user
  99.  
  100. check_S:        ; * * * SEEK 'n' Tracks * * *
  101.     mov    ah,"S"
  102.     call    find
  103.     jne    check_R
  104.     call    get_num            ;get the number of tracks to seek
  105.     je    chk_s            ;number was found, do something
  106.     mov    dx,offset msg_missing
  107.     call    crt
  108.     jmp    short check_R        ;abandon the SEEK routine unless param found
  109. chk_s:    call    SEEK            ;do the Seek test
  110.  
  111. check_R:        ; * * * READ 'n' Tracks * * *
  112.     mov    ah,"R"
  113.     call    find
  114.     jne    check_P
  115.     call    get_num            ;get the number of tracks to seek
  116.     je    chk_r            ;number was found, do something
  117.     mov    dx,offset msg_missing
  118.     call    crt
  119.     jmp    short check_P        ;abandon the READ routine unless param found
  120. chk_r:    call    READ            ;do the Read test
  121.  
  122. check_P:        ; * * * PARK the Selected Drive * * *
  123.     mov    ah,"P"
  124.     call    find
  125.     jne    check_Q
  126.     call    PARK
  127.  
  128. check_Q:        ; * * *  Look for QUESTION Mark * * *
  129.     mov    ah,"?"
  130.     call    find
  131.     jne    done
  132.     call    HELP
  133. done:
  134.     mov    dx,offset msg_blank    
  135.     call    crt            ;clear any pending error msgs 
  136.     mov    dx,offset msg_stack    ;get ready to squeal of bad stack
  137.     mov    cx,cs_save
  138.     pop    ax            ;get CS from stack
  139.     cmp    ax,cx            ;better be equal, or else stack is messed up
  140.     je    exit
  141.     call    crt
  142. exit:    int    20h            ;back to DOS
  143.  
  144. ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;
  145. ;
  146. ;            END of Top Level Program
  147. ;
  148. ;              MAIN Routines Follow
  149. ;
  150.  
  151.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  152.             ; * * * INFOrmation Report * * *
  153. INFO:
  154.     call    show_params
  155.     RET
  156.  
  157.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  158.             ; * * *  Found QUESTION Mark * * *
  159. HELP:
  160.     mov    dx,offset msg_help
  161.     call    crt
  162.     RET
  163.  
  164.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  165.             ; * * * SEEK 'n' Tracks * * *
  166. SEEK:
  167.     call    RECAL
  168.     call    Time_Start
  169.     mov    ax,number
  170.     dec    ax            ;AX has number of tracks, make last tk #
  171.     call    Load_Cyl
  172.     mov    dh,num_heads        ;restore DH-max head number
  173.     mov    dl,drive_sel        ;DL=drive number, 80 or 81
  174.     mov    ah,0Ch
  175.     int    DISK            ;seek specified track
  176.     jnc    seek_1
  177.     jmp    bad_disk
  178. seek_1:
  179.     nop
  180.     nop
  181.     nop
  182.     mov    ah,10h        ;wait while busy
  183.     int    DISK
  184.     jc    seek_1
  185.  
  186.     call    Time_Stop
  187.     mov    dx,offset msg_seektime
  188.     call    crt
  189.     call    Time_Display
  190.     call    Time_Start
  191.     call    RECAL
  192.     call    Time_Stop
  193.     mov    dx,offset msg_recaltime
  194.     call    crt
  195.     call    Time_Display
  196.     call    crlf
  197.     RET
  198.  
  199.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  200.             ; * * * READ 'n' Tracks * * *
  201. READ:
  202.     call    RECAL
  203.  
  204.     mov    ax,offset Buffer    ;find the end of this pgm
  205.     shr    ax,1            ;put it into SEGMENT format
  206.     shr    ax,1            ;put it into SEGMENT format
  207.     shr    ax,1            ;put it into SEGMENT format
  208.     shr    ax,1            ;put it into SEGMENT format
  209.     add    ax,cs_save        ;find out where we are located
  210.     add    ax,1000h        ;flip up to the next segment
  211.     and    ax,0F000h        ;mask off all but next 64k boundary
  212.     mov    es,ax            ;now we have 64k for DMA from disk
  213.  
  214.     call    Time_Start
  215.     mov    cx,number        ;get the number of tracks requested
  216.     cmp    cx,0000            ;
  217.     je    read_loop
  218.     dec    cx            ;AX has number of tracks, make last tk #
  219.  
  220. read_loop:
  221.     push    cx            ;save 'how many times' we want to do this
  222.      mov    ax,cx
  223.      call    Load_Cyl        ;* * *  make sure this sets sector #1
  224.      mov    dh,00            ;starting head number
  225.      mov    al,num_heads        ;get head number
  226.      mul    num_sectors        ;calculate: AL = HEADS * SECTORS = 55h (85d)
  227.      mov    dl,drive_sel        ;DL=drive number, 80 or 81
  228.      mov    bx,000            ;we have 64k to DMA into
  229.      mov    ah,02
  230.      int    DISK            ;read the disc
  231.     pop    cx            ;recover the count of 'tracks to do'
  232.     jnc    read_1
  233.     jmp    bad_disk
  234. read_1:
  235.     mov    ah,10h        ;wait while busy
  236.     int    DISK
  237.     jc    read_1
  238.     loop    read_loop    ;do it 'till they're all read...
  239.  
  240.     call    Time_Stop
  241.     mov    dx,offset msg_numtracks
  242.     call    crt
  243.     mov    ax,number        ;get the number of tracks seeked
  244.     call    ax2dec
  245.     call    crt
  246.     mov    dx,offset msg_readtime
  247.     call    crt
  248.     call    Time_Display
  249.     call    crlf
  250.     RET
  251.  
  252.  
  253.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  254.             ; * * * PARK the Selected Drive * * *
  255. PARK:
  256.     mov    dx,offset msg_recal
  257.     call    crt
  258.     call    RECAL
  259.     mov    dx,offset msg_seek
  260.     call    crt
  261.     mov    dh,num_heads    ;restore DH-max head number
  262.     mov    dl,drive_sel    ;DL=drive number, 80 or 81
  263.     mov    ax,num_tracks
  264.     call    Load_Cyl    ;put cylinder number into CX from AX
  265.     mov    ah,0Ch        ;   SEEK
  266.     int    DISK
  267.     jnc    park4
  268.     jmp    bad_disk
  269.  
  270. park4:
  271.     mov    dx,offset msg_parked
  272.     call    crt
  273.     mov    ax,num_tracks
  274.     call    ax2dec        ;put on CRT the track we parked at
  275.     call    crt
  276.     call    crlf        ;... and leave message on CRT
  277.     RET
  278.  
  279.  
  280. ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;    ;   ;
  281. ;
  282. ;            END of Main Routines
  283. ;
  284. ;          SUBROUTINES and UTILITIES Follow
  285. ;
  286.  
  287.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  288.     ;        These routines manage the TIMER function.  
  289.     ; They start, stop, and display the elapsed time of some disk action.
  290.  
  291. Time_Start:
  292.     push    ax
  293.     push    bx
  294.     push    cx
  295.     push    dx
  296.     mov    ah,2Ch        ;get the time
  297.     int    DOS        ;CH=hrs, CL=min, DH=sec, DL=1/100sec
  298.     mov    dx,0001        ;clear the seconds, the hundredths,
  299.     mov    cl,00        ;...and the minutes
  300.     mov    ah,2Dh        ;send time to system
  301.     int    DOS        ;and reset the system timer
  302.     pop    dx
  303.     pop    cx
  304.     pop    bx
  305.     pop    ax
  306.     Ret
  307.  
  308. Time_Stop:
  309.     push    ax
  310.     push    bx
  311.     push    cx
  312.     push    dx
  313.     mov    ah,2Ch        ;get the time
  314.     int    DOS        ;CH=hrs, CL=min, DH=sec, DL=1/100sec
  315.     mov    num_seconds,DH
  316.     mov    num_100ths,DL
  317.     mov    num_minutes,CL
  318. ;      mov    ax,100        ; Seconds times 100...
  319. ;      mul    num_seconds    ; are now in AX.  This is good to 32 sec
  320. ;      mov    dh,00
  321. ;      add    ax,dx        ;add in the 100ths of a second for a max of 32000 100ths
  322. ;      mov    num_time,ax    ;and save for later use
  323.     pop    dx
  324.     pop    cx
  325.     pop    bx
  326.     pop    ax
  327.     Ret
  328.  
  329. Time_Display:
  330.     push    ax
  331.     push    bx
  332.     push    cx
  333.     push    dx
  334.     push    si
  335.     mov    al,num_minutes
  336.     cmp    al,0            ;don't say MINUTES, if there aren't any
  337.     je    no_mins
  338.     call    al2dec
  339.     call    crt
  340.     mov    dx,offset msg_min
  341.     call    crt
  342. no_mins:
  343.     mov    al,num_seconds
  344.     call    al2dec            ;say how many seconds
  345.     call    crt
  346.     mov    dx,offset msg_sec
  347.     call    crt
  348.     mov    al,num_100ths
  349.     call    al2dec            ;say how many 100ths
  350.     cmp    al,1            ;were there more than 1 digits?
  351.     jg    sec_ok            ;yes, no problem
  352.     dec    dx            ;point previous char position
  353.     mov    si,dx
  354.     mov    al,leading0
  355.     mov    [si],al            ;write the leading zero
  356. sec_ok:
  357.     call    crt
  358.     mov    dx,offset msg_100ths
  359.     call    crt
  360.     pop    si
  361.     pop    dx
  362.     pop    cx
  363.     pop    bx
  364.     pop    ax
  365.     Ret
  366.  
  367.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  368.     ;  Suck the heads out to track zero on the selected drive
  369. Recal:
  370.     mov    dl,drive_sel    ;80 selects first hard disk, 81 the second
  371.     xor    cx,cx        ; * * * should this be:  XOR CX,CX  ?
  372.     mov    ah,11h        ;... recal hard disk
  373.     int    DISK
  374.     jnc    recal_wait
  375.     jmp    bad_disk
  376.  
  377. recal_wait:
  378.     nop
  379.     nop
  380.     nop
  381.     mov    ah,10h        ;wait while busy
  382.     int    DISK
  383.     jc    recal_wait
  384.     RET
  385.  
  386.  
  387.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  388.     ;  Load the Cylinder number into the CX registers for seek
  389.     ;  Enter with the binary track number in the AX.
  390.  
  391. Load_Cyl:
  392.     mov    ch,al        ;put back LSBits
  393.     ror    ah,1
  394.     ror    ah,1        ;rotate MSBits into alignment
  395.     or    ax,0100h    ;sector # = 1
  396.     mov    cl,ah        ;put back MSBits
  397.     RET
  398.  
  399.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  400.     ; Convert the AL register to a Decimal number in the ASCII buffer
  401. AL2Dec:
  402.     mov    ah,00        ;clear hi byte to use AX2DEC routine
  403.  
  404.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  405.     ; Convert the AX register to a Decimal number and put it in the ASCII buffer.
  406.     ; AL returns the length of the ASCII number, DX points to the ASCII string.
  407.     ; The value should probably be less than 32000 for this code to work.
  408. AX2Dec:
  409.     push    cx
  410.     push    bx
  411.     push    si
  412.     mov    si,offset ascii        ;last char position in buffer
  413.     mov    cl,01            ;ASCII character count 
  414.     mov    bx,10            ;decimal divide
  415. ax2dec_loop:
  416.     xor    dx,dx        ;this is where we put the remainder
  417.     div    bx        ;divide by 10
  418.     or    dl,30h        ;convert to ASCII
  419.     mov    [si],DL        ;save digits from right to left
  420.     cmp    ax,0000        ;have we anything to divide, still?
  421.     je    ax_done        ;quit if there is nothing left
  422.     dec    si        ;point to previous location in string for next digit
  423.     inc    cl        ;keep track of number of ASCII characters
  424.     jmp    ax2dec_loop
  425. ax_done:            ;leading zeros are supressed
  426.     mov    dx,si        ; keeps from showing leading spaces
  427.     mov    al,cl        ;recover the number of ASCII chars generated
  428.     pop    si
  429.     pop    bx
  430.     pop    cx
  431.     RET
  432.  
  433.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  434.     ; Disk controller got an error, leave existing message on CRT.
  435.     ; Give BAD message, then controller error number to help debug the problem.
  436.  
  437. Bad_Disk:
  438.     mov    dx,offset bad_msg
  439.     call    crt
  440.     mov    dl,drive_sel
  441.     mov    ah,01                ;get controller status
  442.     int     DISK
  443. ;    mov    al,ah                ;error code may be in AL
  444.     xor    ah,ah
  445.     call    al2dec
  446.     call    crt
  447.     call    crlf
  448.     ret
  449.  
  450.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  451.     ; Get the Parameters of the chosen drive.  They are required
  452.     ; for SHOW_PARAMS, and all routines which seek, park or read
  453.     ; the drive.
  454.  
  455. GET_Params:
  456.     mov    dx,offset msg_notready
  457.     call    crt        ;put up the NOT READY msg in case we aren't
  458.     xor    cx,cx
  459.     mov    dh,ch
  460.     mov    dl,drive_sel    ;80 selects first hard disk, 81 the second
  461.     mov    ah,10h        ;check if drive is ready
  462.     int    DISK
  463.     jnc    get1        ; is ready, so...
  464.     jmp    bad_disk
  465. get1:
  466.     mov    ah,08h        ;get drive params
  467.     int    DISK
  468.     jnc    get3
  469.     jmp    bad_disk
  470. get3:
  471.     push    dx        ;clear the error message from the screen
  472.     mov    dx,offset msg_blank
  473.     call    crt
  474.     pop    dx        ;restore DH - max head number, and DL - number of drives
  475.     mov    ah,cl        ;get MSBits
  476.     rol    ah,1        ;only left two bits are used
  477.     rol    ah,1        ;rotate MSBits into alignment (right 2 bit locations)
  478.     and    ax,0300h    ;mask all but MSBits
  479.     mov    al,ch        ;get LSBits   AX now has # of tracks
  480.     inc    ax        ;correct the count to last (diag) track
  481.     mov    num_tracks,ax    ;save number of tracks
  482.     mov    num_heads,dh    ;save number of heads        0..n-1
  483.     and    cl,00111111b    ;mask out hi bits of track    0..t
  484.     mov    num_sectors,cl    ;save number of sectors        1..s (?)
  485.     mov    num_drives,dl    ;save number of drives         1..2
  486.     RET
  487.  
  488.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  489.     ; Put the following parameters on the display:
  490.     ;  Selected drive, Total Number of drives, number of Tracks,
  491.     ;  number of Heads, number of Sectors.
  492.  
  493. SHOW_Params:
  494.     mov    dx,offset msg_drives
  495.     call    crt            ;how many drives detected
  496.     mov    al,num_drives
  497.     and    al,00000111b
  498.     call    al2dec            ;send AL to CRT in decimal
  499.     call    crt
  500.     mov    dx,offset msg_sel
  501.     call    crt            ;which drive was selected
  502.     mov    al,drive_sel
  503.     and    al,01h
  504.     inc    al            ; 0 = 1st, 1 = 2nd drive
  505.     call    al2dec
  506.     call    crt
  507.     mov    dx,offset msg_tracks
  508.     call    crt            ;number of tracks this drive
  509.     mov    ax,num_tracks        ; already adjusted for diag cyl
  510.     inc    ax            ;is 0..n-1, s/b 1..n
  511.     call    ax2dec            ;send AX to CRT in decimal
  512.     call    crt
  513.     mov    dx,offset msg_heads
  514.     call    crt            ;number of heads this drive
  515.     mov    al,num_heads
  516.     inc    al            ; 1 = 2hds, 3 = 4hds, 4 = 5hds, etc.
  517.     call    al2dec
  518.     call    crt
  519.     mov    dx,offset msg_sectors
  520.     call    crt
  521.     mov    al,num_sectors
  522.     call    al2dec
  523.     call    crt
  524.     call    crlf
  525.     ret
  526.  
  527.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  528.     ; point the DX to your favorite message, then call here for output on CRT
  529. CRT:
  530.     mov    ah,09            ; AX gets wasted
  531.     int    DOS
  532.     ret
  533.  
  534. crt_crlf:
  535.     call    crt
  536. crlf:    mov    dx,offset msg_crlf
  537.     call    crt
  538.     ret
  539.  
  540.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  541.     ; Enter with char in AH to compare against the command line.
  542.     ; Return with Zero Flag set if found and SI pointing past the char.
  543.     ; Return Zero Flag clear if not found anywhere on command line.
  544.     ; All other registers are preserved except SI, CX and flags.
  545.     ; On return, SI points at char if match was made.
  546.     ; The command line is found at CS:81, the length of the line
  547.     ; is at CS:80.  Located at length plus 81 is a Carriage Return
  548.     ; which terminates the line.  All chars have been made UPper case.
  549. FIND:
  550.     cld            ;we want to search forward
  551.     mov    si,offset cmd_line    ;always scan the entire cmd line
  552.     mov    ch,00
  553.     mov    cl,[cmd_len]        ;get the length
  554.     cmp    ch,cl
  555.     je    no_find            ;was of zero length, flag no compare
  556.  
  557. find_slash:
  558.     lodsb            ;get ax,[si] and increment si
  559.     cmp    al,"/"        ;scan fwd 'till we find one
  560.     loopne    find_slash    ;loop here if not at end of line and not eq /
  561.     cmp    cl,ch        ;are we at zero, yet?
  562.     je    no_find
  563.     lodsb            ;get char following slash (/)
  564.     cmp    ah,al
  565.     je    return        ;this is it!  return with 
  566.     loop    find_slash    ;try looking at next one
  567. no_find:
  568.     cmp    ah,0FFh        ;set the NO-COMPARE flag
  569. return:
  570.     ret
  571.  
  572.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  573.     ; Gets a number which may follow a slash-letter parameter.
  574.     ; On entry, SI points past the matched letter in the command line.
  575.     ; CX should still have count to go to end of line.
  576.     ; Output is stored in memory at word NUMBER for use by other routines.
  577.     ; If no number is detected, zero is written into NUMBER and NE=true
  578.     ; All leading characters except slash (/) are skipped till number is
  579.     ; found.  Then number is converted to binary till non-number is found.
  580.  
  581. GET_Num:
  582.     cmp    ch,cl        ;bail out if no more chars on cmd line
  583.     je    no_val
  584.     mov    dx,0000
  585.     mov    number,dx    ;clear the accumulator
  586. pre_scan:            ;throw away all except / till we get a number
  587.     mov    al,[si]        ;get the next char
  588.     cmp    al,'/'        ;bail out if into next command
  589.     je    no_val
  590.     cmp    al,'0'        ;above an ascii 0 ?
  591.     jae    get_dig        ; yes
  592.     cmp    al,'9'        ;below an ascii 9 ?
  593.     jbe    get_dig        ; yes
  594.     inc    si        ;point to next char
  595.     loop    pre_scan    ;no, look at next char if there are more
  596.     jmp    short no_val    ;end of cmd line, bail out
  597. get_dig:
  598.     and    ax,000Fh    ;convert ASCII to      0 < nibble < 10
  599.     mov    bx,ax        ;save the number for later
  600.     mov    al,10        ;prepare to multiply
  601.     mul    number        ; AH is zero, AX gets the result ( < 64k )
  602.     add    ax,bx        ;add in previous value
  603.     mov    number,ax
  604.     inc    si
  605.     mov    al,[si]
  606.     cmp    al,'0'
  607.     jb    get_done
  608.     cmp    al,'9'
  609.     ja    get_done
  610.     jmp    short get_dig
  611. get_done:
  612.     mov    al,0
  613. get_out:
  614.     cmp    al,0
  615.     RET
  616.  
  617. no_val:
  618.     mov    al,0FFh
  619.     jmp    get_out
  620.  
  621.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  622.     ; Convert the entire command line to UPPER case characters
  623.     ; Registers are trashed as may be convenient.
  624. UPper:
  625.     mov    si,offset cmd_line    ;point to first char of Command line
  626.     mov    ch,00
  627.     mov    cl,[cmd_len]        ;get the length of the line
  628.     cmp    ch,cl            ;is the length = zero?
  629.     je    up_done            ;yes, bail out
  630. up_shift:
  631. ;    lodsb        ;cmd_line        ;can't allow SI to get incremented
  632.     mov    al,[si]        ;get a char
  633.     cmp    al,'a'        ;is it lower case?
  634.     jb    up_case        ;no
  635.     cmp    al,'z'
  636.     ja    up_case        ;no
  637.     and    al,0DFh        ;yes, make it upper
  638.     mov    [si],al        ;and write it back into place
  639. up_case:
  640.     inc    si        ;point next char
  641.     loop    up_shift    ;decrement the CX counter, and keep on strokin'
  642. up_done:
  643.     mov    al,'/'
  644.     mov    [si],al        ;mark the end of the string
  645.     RET
  646.  
  647.     ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  648.     ;    Temporary routine to indicate that some Top Level
  649.     ;    programs above have not been completed.
  650.  
  651. not_done:
  652.     mov    dx,offset msg_notdone
  653.     call    crt
  654.     Ret
  655.  
  656. msg_notdone    db    cr,lf,"The selected function has not yet been implemented.",cr,lf,eof
  657.  
  658. sign_on        db    "HD - a Hard Disk Test Program.  Copyright 1985 by R. Steincross.  Vers 0.2 ",cr,lf,eof
  659. msg_notready    db    "Disk Drive Not Ready.    ",cr,eof
  660. msg_parm    db    "Couldn't get Drive Parameters.    ",cr,eof
  661. msg_blank    db    "                ",cr,eof
  662. msg_2nd        db    "The SECOND Disc Drive has been selected.    ",cr,lf,eof
  663. msg_recal    db    "Slow Seek to Track Zero.    ",cr,eof
  664. msg_seek    db    "Fast Seeking to Final Track.    ",cr,eof
  665. msg_parked    db    "The drive has been sent to the last (diagnostic) cylinder, track number ",eof
  666. msg_floppy    db    "The FLOPPY drives have been selected.",cr,lf,eof
  667. msg_stack    db    "The 8086 stack was not properly cleaned up on exit.",cr,lf,bell,eof
  668. msg_missing    db    "Missing Parameter.  Command Ignored.    ",cr,lf,eof
  669.  
  670. msg_seektime    db    cr,lf,"Time to seek from zero to desired track: ",eof
  671. msg_recaltime    db    cr,lf,"Time to slow seek back to track zero:    ",eof
  672.  
  673. msg_min        db    " minutes, ",eof
  674. msg_sec        db    ".",eof
  675. msg_100ths    db    " seconds.",eof
  676.  
  677. msg_drives    db    cr,lf,"Hard drives found: ",eof
  678. msg_sel        db    ", Drive Selected: ",eof
  679. msg_tracks    db    ", TRACKs: ",eof
  680. msg_heads    db    ", HEADs: ",eof
  681. msg_sectors    db    ", SECTORs: ",eof
  682. msg_crlf    db    cr,lf,eof
  683.  
  684. msg_numtracks    db    "Number of tracks read: ",eof
  685. msg_readtime    db    ".  Elapsed time: ",eof
  686.  
  687. bad_msg      db    cr,lf,"Either the program or controller failed.  Controller Error Code: ",bell,eof
  688.  
  689. msg_help    db    cr,lf
  690.     db    "This program uses the slash commands shown below to determine the    ",cr,lf
  691.     db    "performance of a hard disk drive or controller.  If a /2 is detected    ",cr,lf
  692.     db    "on the command line, then all testing is directed to the second drive.    ",cr,lf
  693.     db    "The commands are listed in the order they will be executed.        ",cr,lf,lf
  694.  
  695.     db    "    /2    Use the second drive instead of the first.        ",cr,lf
  696. ;    db    "    /F    The Floppy drive is selected instead of hard disk.    ",cr,lf
  697.     db    "    /I    INFOrmation about the drive is reported.        ",cr,lf
  698.     db    "    /S n    SEEK test from 0 to 'n' and Recal from 'n' to 0.    ",cr,lf
  699.     db    "    /R n    READ 'n' Tracks and report the time taken.        ",cr,lf
  700.     db    "    /P    PARK the drive on the last track.            ",cr,lf
  701.     db    "    /?    HELP screen displayed.                    ",cr,lf,eof
  702.  
  703. Buffer    dw    0        ;this is the start of the read buffer
  704.  
  705. HD    endp            
  706. code_seg    ends        
  707.     end    entry        
  708.  
  709. ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
  710.  
  711.     On entry to the Hard Disk ROM Bios, AH is set with one of the
  712.     following values, depending of the function requested:
  713.     AH = 00        Reset the hard disk
  714.     AH = 08        Get the drive parameters
  715.     AH = 0Ch    Seek to the desired track
  716.  
  717.     Additional registers which may have to be set on entry are:
  718.     DH = 0..7    Head number
  719.     DL = 80..87h    Drive number where 80 = first hard drive 
  720.     CH = 0..1023    Low byte of Cylinder number
  721.     CL = cc-sssss    Sector number and high bits of Cylinder number
  722.     AL = 1..80h    Number of sectors  (or interleave for format)
  723.     ES:BX        Address of buffer for reads and writes
  724.  
  725.     In the event of a failure, the AH returns the error code, and
  726.     the carry bit is set.  If no falilure, CY=0 and AH=0.
  727.  
  728.     If Drive Parameters are requested, they are returned as follows:
  729.     DH = 0..h-1    Maximum head number
  730.     DL = 1..2    Number of hard drives attached
  731.     CH = 1..1023    Max useable cylinder number, low byte
  732.     CL = cc-sssss    Max useable sector number and hi bits of cyl
  733.  
  734.